package cn.sc.summer.core.filter.custom;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.sc.summer.constant.util.AssertUtil;
import cn.sc.summer.exception.exceptions.CommonException;
import lombok.Data;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.lang.NonNull;

import java.io.Serializable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Predicate;

/**
 * 类名：过滤连调用
 * T:过滤连入参数据类型，可为null
 *
 * @author a-xin
 * @date 2024/4/1 17:13
 */
@Slf4j
@Data
@Accessors(chain = true)
public class FilterXChain<T> implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 过滤连
     */
    private ConcurrentHashMap<FilterX<T>, Predicate<T>> filters = new ConcurrentHashMap<>();

    /**
     * 过滤连id
     */
    private String chainId = IdUtil.simpleUUID();

    /**
     * 过滤连名称
     */
    private String chainName = "FilterX[" + RandomUtil.randomString(16) + "]";

    /**
     * 过滤连长度
     */
    private Integer length = 0;

    /**
     * 当前执行过滤连位置
     */
    private Integer current = 0;

    /**
     * 添加拦截执行条件
     *
     * @param filterX   过滤器实现
     * @param predicate 断言
     * @return 过滤连
     */
    public FilterXChain<T> addFilterX(@NonNull FilterX<T> filterX, @NonNull Predicate<T> predicate) {
        this.length = filters.size();
        this.filters.put(filterX, predicate);
        return this;
    }

    /**
     * 添加过滤连
     *
     * @param filterX 过滤器实现
     * @return 过滤连
     */
    public FilterXChain<T> addFilterX(@NonNull FilterX<T> filterX) {
        this.length = filters.size();
        this.filters.put(filterX, param -> Boolean.TRUE);
        return this;
    }

    public void doFilter(T param) {
        AssertUtil.functionIfPre(filters, CollUtil::isNotEmpty, () -> {
            filters.forEach((filterX, predicate) -> {
                current++;
                if ((ObjectUtil.isNotNull(predicate) && predicate.test(param)) || ObjectUtil.isNull(predicate)) {
                    log.info("==> Start run filter: " + chainName + "[" + this.current + "]: " + filterX.getFilterId());
                    try {
                        filterX.doFilter(param);
                    } catch (Throwable e) {
                        log.error("==> The filter: " + chainName + "[" + this.current + "]: " + filterX.getFilterId() + " catch an exception: " + e.getMessage());
                        throw new CommonException(e.getMessage(), e);
                    }
                }
            });
            log.warn("==> The filters chain named " + chainName + " is executed!");
        }, () -> log.warn("==> The filters chain named " + chainName + " has no filters!"));
    }

}

